package com.jacky.demo.opengl;

import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;

import com.jacky.log.Logger;

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import static com.jacky.demo.opengl.GlUtils.allocateFloatBuffer;
import static com.jacky.demo.opengl.GlUtils.bindTexture;
import static com.jacky.demo.opengl.GlUtils.cameraBackVertexData;
import static com.jacky.demo.opengl.GlUtils.cameraFrontVertexData;
import static com.jacky.demo.opengl.GlUtils.cameraTextureData;
import static com.jacky.demo.opengl.GlUtils.createProgram;
import static com.jacky.demo.opengl.GlUtils.createTextureID;
import static com.jacky.demo.opengl.GlUtils.fragmentShaderCode;
import static com.jacky.demo.opengl.GlUtils.vertexShaderCode;
import static com.jacky.demo.opengl.GlUtils.waterMaskTextureData;
import static com.jacky.demo.opengl.GlUtils.waterMaskVertexData;

/**
 * Created by lixinquan on 2019/2/18.
 */
class GLCamera extends Shape {

    private Camera mCamera;

    int mProgramWaterMask, mProgramGl;
    int textureID, waterMaskID, waterMaskWidth, waterMaskHeight, screenWidth, screenHeight;
    SurfaceTexture mSurfaceTextrue;
    int currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;

    private FloatBuffer cameraVertexBuffer, cameraTextureBuffer;
    private FloatBuffer waterVertexBuffer, waterTextureBuffer;
    private CameraGLSurfaceView.PictureCallback mCallback;

    protected String getVertexShader() {
        return vertexShaderCode;
    }

    protected String getFragmentShader() {
        return fragmentShaderCode;
    }

    GLCamera(Bitmap bitmap) {
        cameraTextureBuffer = allocateFloatBuffer(cameraTextureData);

        mProgramGl = createProgram(GlUtils.waterVertexShader, GlUtils.waterFragmentShader);
        mProgramWaterMask = createProgram(GlUtils.waterVertexShader, GlUtils.waterFragmentShader);
        waterMaskWidth = bitmap.getWidth();
        waterMaskHeight = bitmap.getHeight();
        waterMaskID = bindTexture(bitmap);
        waterTextureBuffer = allocateFloatBuffer(waterMaskTextureData);
        waterVertexBuffer = allocateFloatBuffer(waterMaskVertexData);
    }

    private int[] fFrame = new int[1];
    int[] fTexture = new int[1];

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        textureID = createTextureID();
        mSurfaceTextrue = new SurfaceTexture(textureID);
        open(currentCameraId);
    }

    @Override
    void onSurfaceChanged(GL10 gl, int width, int height) {
        this.screenWidth = width;
        this.screenHeight = height;
        bindFrameTexture();
    }

    void takePicture(CameraGLSurfaceView.PictureCallback callback) {
        mCallback = callback;
    }

    void switchCamera() {
        currentCameraId =
                currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK ?
                Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
        open(currentCameraId);
    }

    int getFitFrameRate(int rate) {
        List<Integer> rates = mCamera.getParameters().getSupportedPreviewFrameRates();
        int r = rates.contains(rate) ? rate : rates.get(0);
        return r;
    }

    @Override
    void draw() {
        if(mSurfaceTextrue != null)
            mSurfaceTextrue.updateTexImage();

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0]);
        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, fTexture[0], 0);
        GLES20.glViewport(0,0, screenWidth, screenHeight);
        //绘制摄像头的图像
        drawGLTexture(true, mProgram, textureID, cameraTextureBuffer, cameraVertexBuffer);

        //绘制水印
        GLES20.glViewport(10, 200, waterMaskWidth, waterMaskHeight);
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        drawGLTexture(false, mProgramWaterMask, waterMaskID, waterTextureBuffer, waterVertexBuffer);
        GLES20.glDisable(GLES20.GL_BLEND);

        if(GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) {
            Logger.w("ERROR::FRAMEBUFFER:: Framebuffer is not complete!");
        }
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);

        GLES20.glViewport(0,0, screenWidth, screenHeight);
        drawGLTexture(false, mProgramGl, fTexture[0], GlUtils.textureBuffer, GlUtils.vertexBuffer);

        if(mCallback != null) {
            Bitmap result = drawToBitmap();
            mCallback.onPictureTaken(result);
            mCallback = null;
            result.recycle();
        }
    }

    public void open(int cameraId) {
        stopCamera();
        if(Camera.getNumberOfCameras() == 1) {
            mCamera = Camera.open();
            cameraVertexBuffer = allocateFloatBuffer(cameraVertexBuffer, cameraBackVertexData);
        } else {
            mCamera = Camera.open(cameraId);
            cameraVertexBuffer = allocateFloatBuffer(cameraVertexBuffer,
                    cameraId == Camera.CameraInfo.CAMERA_FACING_BACK ? cameraBackVertexData : cameraFrontVertexData);
        }
        if (mCamera != null) {
            /**选择当前设备允许的预览尺寸*/
            Camera.Parameters param = mCamera.getParameters();
            Camera.Size size = param.getPreviewSize();
            Logger.w("preview size", size.width, size.height);
//            param.setPictureFormat(PixelFormat.JPEG);//设置拍照后存储的图片格式
//            mCamera.setDisplayOrientation(90);
//            param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
//            preSize = getPropPreviewSize(param.getSupportedPreviewSizes(), mConfig.rate,
//                    mConfig.minPreviewWidth);
//            picSize = getPropPictureSize(param.getSupportedPictureSizes(),mConfig.rate,
//                    mConfig.minPictureWidth);
//            param.setPictureSize(picSize.screenWidth, picSize.screenHeight);
//            param.setPreviewSize(preSize.screenWidth,preSize.screenHeight);

            mCamera.setParameters(param);
//            Camera.Size pre=param.getPreviewSize();
//            Camera.Size pic=param.getPictureSize();
//            mPicSize=new Point(pic.screenHeight,pic.screenWidth);
//            mPreSize=new Point(pre.screenHeight,pre.screenWidth);
            setPreviewTexture(mSurfaceTextrue);
            mCamera.startPreview();
        }
    }

    @Override
    public void release() {
        super.release();
        GLES20.glDeleteProgram(mProgramGl);
        GLES20.glDeleteProgram(mProgramWaterMask);
        GLES20.glDeleteFramebuffers(1, fFrame, 0);
        GLES20.glDeleteTextures(1, fTexture, 0);
    }

    private void setPreviewTexture(SurfaceTexture surfaceTexture) {
        try {
            mCamera.setPreviewTexture(surfaceTexture);
        } catch (IOException e) {
            Logger.e(e);
        }
    }

    public void pause() {
        stopCamera();
        if(mSurfaceTextrue != null) {
            mSurfaceTextrue.setOnFrameAvailableListener(null);
            mSurfaceTextrue.release();
            mSurfaceTextrue = null;
        }
    }

    private void stopCamera() {
        if(mCamera != null) {
            setPreviewTexture(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    public boolean isFlashLightOn() {
        Camera.Parameters parameters = mCamera.getParameters();
        return !Camera.Parameters.FLASH_MODE_OFF.equals(parameters.getFlashMode());
    }

    public boolean toggleFlashLight() {
        if(mCamera == null) return false;
        Camera.Parameters parameters = mCamera.getParameters();
        // 判断闪光灯当前状态來修改
        boolean on;
        if (Camera.Parameters.FLASH_MODE_OFF.equals(parameters.getFlashMode())) {
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
            on = true;
        } else {
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
            on = false;
        }
        mCamera.setParameters(parameters);
        return on;
    }

    private void bindFrameTexture() {
        GLES20.glDeleteFramebuffers(1, fFrame, 0);
        GLES20.glDeleteTextures(1, fTexture, 0);
        /**创建一个帧染缓冲区对象*/
        GLES20.glGenFramebuffers(1,fFrame,0);
        /**根据纹理数量 返回的纹理索引*/
        GLES20.glGenTextures(1, fTexture, 0);
        /**将生产的纹理名称和对应纹理进行绑定*/
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fTexture[0]);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,0, GLES20.GL_RGBA, screenWidth, screenHeight,0,GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,null);
        GlUtils.glTexParameter(false);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
    }

    private void drawGLTexture(boolean isOes, int p, int texture, Buffer texureBuffer, Buffer vertextBuffer) {
        GLES20.glUseProgram(p);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        if(isOes) {
            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
        } else {
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
        }
        int mPositionHandle = GLES20.glGetAttribLocation(p, "af_Position");
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        GLES20.glVertexAttribPointer(mPositionHandle, 2, GLES20.GL_FLOAT, false, 8, texureBuffer);
        //纹理坐标
        int mTextureCoordHandle = GLES20.glGetAttribLocation(p, "av_Position");
        GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
        GLES20.glVertexAttribPointer(mTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 8, vertextBuffer);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
    }

    private Bitmap drawToBitmap() {
        int width = screenWidth, height = screenHeight, W = width * 4;
        long start = System.currentTimeMillis();
        ByteBuffer pixelBuf = ByteBuffer.allocateDirect(W * height);
        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuf);
        byte[] bytes = pixelBuf.array();
        byte temp;
        int i, j;
        //因为读取出来的图片是上下颠倒的，所以需要转换下数据，保存正常的图片位置
        for(int h = 0;h < height /2;h++) {
            for (int w = 0; w < W;w++) {
                i = h * W + w;
                j = (height - h - 1) * W + w;
                temp = bytes[i];
                bytes[i] = bytes[j];
                bytes[j] = temp;
            }
        }
        Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        result.copyPixelsFromBuffer(pixelBuf);
        long end = System.currentTimeMillis();
        Logger.w("cost time", end -start);
        return result;
    }
}
